#pragma once

#include <GL/glew.h>
#include "BicubicUniformPeriodicBSplinePatch.h"
#include "Materials.h"
#include "Matrices.h"
#include "TriangulatedMeshes.h"
#include <vector>

namespace cagd
{
    typedef struct _NeighborPatch{
        BicubicUniformPeriodicBSplinePatch *patch;
        int index;
        int row;
        int column;
    }NeighborPatch;

    class BicubicUniformPeridocBSplineSurface
    {
    protected:
        GLuint                                          _vbo_control_net;
        Matrix<DCoordinate>                             _control_net;
        GLenum                                          _usage_flage;
        Material&                                       _default_material;
        GLboolean                                       _is_closed_u_direction;
        GLboolean                                       _is_closed_v_direction;
        GLuint                                          _u_div_point_count;
        GLuint                                          _v_div_point_count;

        std::vector<BicubicUniformPeriodicBSplinePatch> _patches;
        std::vector<TriangulatedMesh>                   _meshes;
        std::vector<Material>                           _materials;

        Matrix<std::vector<NeighborPatch> > neighbor_patches;
        Matrix<std::vector<DCoordinate *> > same_control_points;

    public:
        BicubicUniformPeridocBSplineSurface(GLuint row_count = 4,
                                            GLuint column_count = 4,
                                            GLboolean is_closed_u_direction = false,
                                            GLboolean is_closed_v_direction = false,
                                            GLuint u_div_point_count = 30,
                                            GLuint v_div_point_count = 30,
                                            GLenum usage_flag = GL_STATIC_DRAW,
                                            Material &default_material = MatFBSilver);

        BicubicUniformPeridocBSplineSurface(const BicubicUniformPeridocBSplineSurface& surface);

        BicubicUniformPeridocBSplineSurface& operator =(const BicubicUniformPeridocBSplineSurface& rhs);

        GLboolean SetControlPoint(GLuint row, GLuint column,
                                   const DCoordinate &p,
                                   GLboolean apply_to_patch,
                                   GLenum usage_flage = GL_STATIC_DRAW);
        GLboolean SetControlPoint(GLuint row, GLuint column,
                                  GLdouble x, GLdouble y, GLdouble z,
                                  GLboolean apply_to_patch,
                                  GLenum usage_flage = GL_STATIC_DRAW);
        GLboolean SetControlPoint(GLuint index,
                                  GLdouble x, GLdouble y, GLdouble z,
                                  GLboolean apply_to_patch,
                                  GLenum usage_flage = GL_STATIC_DRAW);
        GLboolean SetControlPoint(GLuint index,
                                  const DCoordinate &p,
                                  GLboolean apply_to_patch,
                                  GLenum usage_flage = GL_STATIC_DRAW);

        GLboolean GetControlPoint(GLuint row, GLuint column, DCoordinate &p) const;
        GLboolean GetControlPoint(GLuint row, GLuint column, GLdouble &x, GLdouble &y, GLdouble &z) const;
        GLboolean GetControlPoint(GLuint index, GLdouble &x, GLdouble &y, GLdouble &z) const;
        GLboolean GetControlPoint(GLuint index, DCoordinate &p) const;

        GLboolean SetMaterial(GLuint patch_index, const Material &m);

        GLboolean BuildAllPatches(GLuint u_div_point_count, GLuint v_div_point_count, GLenum usage_flag = GL_STATIC_DRAW);

        GLboolean BuildASinglePatch(GLuint patch_index, GLuint u_div_point_count, GLuint v_div_point_count, GLenum usage_flag = GL_STATIC_DRAW);

        GLboolean Render(GLenum render_mode = GL_TRIANGLES) const;

        // a nagy kontrollhalo vbo-val kapcsolatos metodusok
        GLvoid DeleteVBOOfControlNet();
        GLboolean RenderControlNet(GLenum render_mode = GL_LINE_STRIP) const;
        GLboolean UpdateVBOOfControlNet(GLenum usage_type = GL_STATIC_DRAW);

        // egy adott patch kontrollhalojanak megjelenitese
        GLboolean UpdateVBOOfControlNetOfAPatch(GLuint patch_index, GLenum usage_type = GL_STATIC_DRAW);
        GLboolean RenderControlNetOfAPatch(GLuint patch_index, GLenum render_mode = GL_LINE_STRIP) const;

        GLuint GetControlPointCount();

        // a nagy kontrollhalo vbo-nak felszabaditasa, stb.
        virtual ~BicubicUniformPeridocBSplineSurface();
    };
}
